/* Copyright Thanh-Trung Pham, JAIST, 2012 */

package jp.ac.jaist.computation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import jp.ac.jaist.BranchingStructureType;
import jp.ac.jaist.CallPropagationType;
import jp.ac.jaist.CallingActivityType;
import jp.ac.jaist.ComponentConnectorType;
import jp.ac.jaist.ComponentInstanceType;
import jp.ac.jaist.InternalActivityType;
import jp.ac.jaist.LoopingStructureType;
import jp.ac.jaist.ParallelStructureType;
import jp.ac.jaist.PrimaryBackupFaultToleranceStructureType;
import jp.ac.jaist.SequentialStructureType;
import jp.ac.jaist.ServiceImplementationType;
import jp.ac.jaist.UsageProfile.Probability;
import jp.ac.jaist.UserInterface;
import jp.ac.jaist.input.RMInputModelManager;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.lang3.StringUtils;

/**
 *
 * @author Thanh-Trung Pham
 */
public class RMSensitivityAnalyser {

    private static final String RM_IA = "ia";
    private static final int RM_NUMBER_OF_SAMPLE_FOR_USAGE_SENSITIVITY_ANALYSIS = 11;
    private static final int RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS = 11;
    private static final String RM_SEMICOLON = ";";
    private static RMSensitivityAnalyser m_oRMSensitivityAnalyser;
    private String m_strUserInterfaceName;
    private List<String> m_oExploredProvidedServices;
    private MultiValueMap m_oIAsOfProvidedServices;
    private MultiValueMap m_oLoopingCountsOfProvidedServices;
    private MultiValueMap m_oBranchingConditionsOfProvidedServices;
    private LinkedList<String> m_oTODOProvidedServices;

    /**
     * The constructor.
     */
    private RMSensitivityAnalyser() {
        m_oExploredProvidedServices = new ArrayList<String>();
        m_oIAsOfProvidedServices = new MultiValueMap();
        m_oBranchingConditionsOfProvidedServices = new MultiValueMap();
        m_oLoopingCountsOfProvidedServices = new MultiValueMap();
        m_oTODOProvidedServices = new LinkedList<String>();
    }

    /**
     * Do the sensitivity analysis.
     * @param userInterfaceName the user interface name.
     * @return all the sensitivity analysis data.
     */
    public RMSensitivityAnalysisData[] doSensitivityAnalysis(String userInterfaceName) {
        List<RMSensitivityAnalysisData> oReturnData = new ArrayList<RMSensitivityAnalysisData>();

        exploreUserInterface(userInterfaceName);
        oReturnData.add(doSensitivityAnalysisToUsageProbabilities());

        oReturnData.addAll(doSensitivityAnalysisToReliabilityRelatedProbabilities());

        return (RMSensitivityAnalysisData[]) oReturnData.toArray(new RMSensitivityAnalysisData[oReturnData.size()]);
    }

    /**
     * Do the sensitivity analysis to usage probabilities.
     * @return the sensitivity analysis data.
     */
    private RMSensitivityAnalysisData doSensitivityAnalysisToUsageProbabilities() {
        double dMaxVaryingValue = determineMaxVaryingValueForUsageProbabilities();
        double dVaryingStep = dMaxVaryingValue / (RM_NUMBER_OF_SAMPLE_FOR_USAGE_SENSITIVITY_ANALYSIS - 1);

        RMSensitivityAnalysisData oData = new RMSensitivityAnalysisData("Sensitivity to usage probabilities", "Service reliability vs. Usage probabilities", "Usage probability", "Service reliability");
        for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_USAGE_SENSITIVITY_ANALYSIS; i++) {
            oData.addAxisXValue(dVaryingStep * i);
        }

        RMInputModelManager oManager = RMInputModelManager.getInstance();
        RMReliabilityCalculator oCalculator = RMReliabilityCalculator.getInstance();

        Iterator oKeyIterator = m_oBranchingConditionsOfProvidedServices.keySet().iterator();
        while (oKeyIterator.hasNext()) {
            String strFullProvidedServiceName = (String) oKeyIterator.next();

            Collection<String> oBranchingConditionsOfProvidedService = (Collection<String>) m_oBranchingConditionsOfProvidedServices.get(strFullProvidedServiceName);
            Iterator<String> oValueIterator = oBranchingConditionsOfProvidedService.iterator();

            while (oValueIterator.hasNext()) {
                String[] oBranchingConditionsOfBranchingStructure = StringUtils.split(oValueIterator.next(), RM_SEMICOLON);

                for (String strBranchingCondition : oBranchingConditionsOfBranchingStructure) {
                    Probability oProbability = oManager.getUsageProfileProbability(m_strUserInterfaceName, strBranchingCondition);
                    double dBackedUpValue = oProbability.getValue();
                    try {
                        for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_USAGE_SENSITIVITY_ANALYSIS; i++) {
                            oProbability.setValue(dVaryingStep * i);
                            InternalActivityType oIA = oCalculator.calculateReliabilityRelatedProbabilities(m_strUserInterfaceName);

                            oData.addAxisYValue(strFullProvidedServiceName, strBranchingCondition, (1.0 - oIA.getSfp() - oIA.getPfp()));
                        }
                    } finally {
                        oProbability.setValue(dBackedUpValue);
                    }
                }
            }
        }

        return oData;
    }

    /**
     * Do the sensitivity analysis to reliability related probabilities.
     * @return all the sensitivity analysis data.
     */
    private List<RMSensitivityAnalysisData> doSensitivityAnalysisToReliabilityRelatedProbabilities() {

        MaxVaryingValuesForReliabilityRelatedProbabilities oMaxVaryingValues = determineMaxVaryingValueForReliabilityRelatedProbabilities();

        double dVaryingStepForSfp = oMaxVaryingValues.getMaxVaryingValue(0) / (RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS - 1);
        double dVaryingStepForPfp = oMaxVaryingValues.getMaxVaryingValue(1) / (RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS - 1);
        double dVaryingStepForSep = oMaxVaryingValues.getMaxVaryingValue(2) / (RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS - 1);
        double dVaryingStepForNmep = oMaxVaryingValues.getMaxVaryingValue(3) / (RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS - 1);

        RMSensitivityAnalysisData oDataForSfp = new RMSensitivityAnalysisData("Sensitivity to stopping failure probabilities (sfp)", "Service reliability vs. Sfp of internal activities", "Sfp of internal activity", "Service reliability");
        RMSensitivityAnalysisData oDataForPfp = new RMSensitivityAnalysisData("Sensitivity to propagating failure probabilities (pfp)", "Service reliability vs. Pfp of internal activities", "Pfp of internal activity", "Service reliability");
        RMSensitivityAnalysisData oDataForSep = new RMSensitivityAnalysisData("Sensitivity to stopping error probabilities (sep)", "Service reliability vs. Sep of internal activities", "Sep of internal activity", "Service reliability");
        RMSensitivityAnalysisData oDataForNmep = new RMSensitivityAnalysisData("Sensitivity to non-masking error probabilities (nmep)", "Service reliability vs. Nmep of internal activities", "Nmep of internal activity", "Service reliability");

        for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
            oDataForSfp.addAxisXValue(dVaryingStepForSfp * i);
        }

        for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
            oDataForPfp.addAxisXValue(dVaryingStepForPfp * i);
        }

        for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
            oDataForSep.addAxisXValue(dVaryingStepForSep * i);
        }

        for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
            oDataForNmep.addAxisXValue(dVaryingStepForNmep * i);
        }

        RMReliabilityCalculator oCalculator = RMReliabilityCalculator.getInstance();

        Iterator oKeyIterator = m_oIAsOfProvidedServices.keySet().iterator();
        while (oKeyIterator.hasNext()) {
            String strFullProvidedServiceName = (String) oKeyIterator.next();

            Collection<InternalActivityType> oIAsOfProvidedService = (Collection<InternalActivityType>) m_oIAsOfProvidedServices.get(strFullProvidedServiceName);
            Iterator<InternalActivityType> oValueIterator = oIAsOfProvidedService.iterator();

            while (oValueIterator.hasNext()) {
                InternalActivityType oCurrentIA = oValueIterator.next();

                String strIAName = oCurrentIA.getName() != null ? oCurrentIA.getName() : RM_IA;
                
                double dBackedUpValueForSfp = oCurrentIA.getSfp();
                try {
                    for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
                        oCurrentIA.setSfp(dVaryingStepForSfp * i);
                        InternalActivityType oEquivalentIA = oCalculator.calculateReliabilityRelatedProbabilities(m_strUserInterfaceName);

                        oDataForSfp.addAxisYValue(strFullProvidedServiceName, strIAName, (1.0 - oEquivalentIA.getSfp() - oEquivalentIA.getPfp()));
                    }
                } finally {
                    oCurrentIA.setSfp(dBackedUpValueForSfp);
                }

                double dBackedUpValueForPfp = oCurrentIA.getPfp();
                try {
                    for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
                        oCurrentIA.setPfp(dVaryingStepForPfp * i);
                        InternalActivityType oEquivalentIA = oCalculator.calculateReliabilityRelatedProbabilities(m_strUserInterfaceName);

                        oDataForPfp.addAxisYValue(strFullProvidedServiceName, strIAName, (1.0 - oEquivalentIA.getSfp() - oEquivalentIA.getPfp()));
                    }
                } finally {
                    oCurrentIA.setPfp(dBackedUpValueForPfp);
                }

                double dBackedUpValueForSep = oCurrentIA.getSep();
                try {
                    for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
                        oCurrentIA.setSep(dVaryingStepForSep * i);
                        InternalActivityType oEquivalentIA = oCalculator.calculateReliabilityRelatedProbabilities(m_strUserInterfaceName);

                        oDataForSep.addAxisYValue(strFullProvidedServiceName, strIAName, (1.0 - oEquivalentIA.getSfp() - oEquivalentIA.getPfp()));
                    }
                } finally {
                    oCurrentIA.setSep(dBackedUpValueForSep);
                }

                double dBackedUpValueForNmep = oCurrentIA.getNmep();
                try {
                    for (int i = 0; i < RM_NUMBER_OF_SAMPLE_FOR_RELIABILITY_RELATED_SENSITIVITY_ANALYSIS; i++) {
                        oCurrentIA.setNmep(dVaryingStepForNmep * i);
                        InternalActivityType oEquivalentIA = oCalculator.calculateReliabilityRelatedProbabilities(m_strUserInterfaceName);

                        oDataForNmep.addAxisYValue(strFullProvidedServiceName, strIAName, (1.0 - oEquivalentIA.getSfp() - oEquivalentIA.getPfp()));
                    }
                } finally {
                    oCurrentIA.setNmep(dBackedUpValueForNmep);
                }

            }
        }

        List<RMSensitivityAnalysisData> oReturnData = new ArrayList<RMSensitivityAnalysisData>();
        oReturnData.add(oDataForSfp);
        oReturnData.add(oDataForPfp);
        oReturnData.add(oDataForSep);
        oReturnData.add(oDataForNmep);

        return oReturnData;
    }

    /**
     * Determine the maximum varying value for usage probabilities.
     * @return the maximum varying value for usage probabilities.
     */
    private double determineMaxVaryingValueForUsageProbabilities() {
        double dMaxVaryingValue = 1.0;

        RMInputModelManager oManager = RMInputModelManager.getInstance();

        Iterator oKeyIterator = m_oBranchingConditionsOfProvidedServices.keySet().iterator();
        while (oKeyIterator.hasNext()) {
            String strFullProvidedServiceName = (String) oKeyIterator.next();

            Collection<String> oBranchingConditionsOfProvidedService = (Collection<String>) m_oBranchingConditionsOfProvidedServices.get(strFullProvidedServiceName);
            Iterator<String> oValueIterator = oBranchingConditionsOfProvidedService.iterator();

            while (oValueIterator.hasNext()) {
                String[] oBranchingConditionsOfBranchingStructure = StringUtils.split(oValueIterator.next(), RM_SEMICOLON);

                double dTotalProbabilityOfIfBranches = 0.0;

                for (String strBranchingCondition : oBranchingConditionsOfBranchingStructure) {
                    Double oProbability = oManager.getUsageProfileProbabilityValue(m_strUserInterfaceName, strBranchingCondition);
                    dTotalProbabilityOfIfBranches += oProbability.doubleValue();
                }

                double dProbabilityOfElseBranch = 1.0 - dTotalProbabilityOfIfBranches;

                for (String strBranchingCondition : oBranchingConditionsOfBranchingStructure) {
                    Double oProbability = oManager.getUsageProfileProbabilityValue(m_strUserInterfaceName, strBranchingCondition);

                    if (dProbabilityOfElseBranch + oProbability.doubleValue() < dMaxVaryingValue) {
                        dMaxVaryingValue = dProbabilityOfElseBranch + oProbability.doubleValue();
                    }
                }

            }
        }

        return dMaxVaryingValue;
    }

    /**
     * Determine the maximum varying values for reliability related probabilities.
     * @return the maximum varying values for reliability related probabilities.
     */
    private MaxVaryingValuesForReliabilityRelatedProbabilities determineMaxVaryingValueForReliabilityRelatedProbabilities() {
        double dMaxVaryingValueForSfp = 1.0;
        double dMaxVaryingValueForSep = 1.0;
        double dMaxVaryingValueForPfp = 1.0;
        double dMaxVaryingValueForNmep = 1.0;

        Iterator oKeyIterator = m_oIAsOfProvidedServices.keySet().iterator();
        while (oKeyIterator.hasNext()) {
            String strFullProvidedServiceName = (String) oKeyIterator.next();

            Collection<InternalActivityType> oIAsOfProvidedService = (Collection<InternalActivityType>) m_oIAsOfProvidedServices.get(strFullProvidedServiceName);
            Iterator<InternalActivityType> oValueIterator = oIAsOfProvidedService.iterator();

            while (oValueIterator.hasNext()) {
                InternalActivityType oIA = oValueIterator.next();

                double dReliability = (1.0 - oIA.getSfp() - oIA.getPfp());
                double dMaskingErrorProbability = (1.0 - oIA.getSep() - oIA.getNmep());

                if (oIA.getSfp() + dReliability < dMaxVaryingValueForSfp) {
                    dMaxVaryingValueForSfp = oIA.getSfp() + dReliability;
                }

                if (oIA.getPfp() + dReliability < dMaxVaryingValueForPfp) {
                    dMaxVaryingValueForPfp = oIA.getPfp() + dReliability;
                }

                if (oIA.getSep() + dMaskingErrorProbability < dMaxVaryingValueForSep) {
                    dMaxVaryingValueForSep = oIA.getSep() + dMaskingErrorProbability;
                }

                if (oIA.getNmep() + dMaskingErrorProbability < dMaxVaryingValueForNmep) {
                    dMaxVaryingValueForNmep = oIA.getNmep() + dMaskingErrorProbability;
                }
            }
        }

        return new MaxVaryingValuesForReliabilityRelatedProbabilities(dMaxVaryingValueForSfp, dMaxVaryingValueForPfp, dMaxVaryingValueForSep, dMaxVaryingValueForNmep);
    }

    /**
     * Get all the branching conditions of provided services.
     * @return all the branching conditions of provided services.
     */
    public MultiValueMap getBranchingConditionsOfProvidedServices() {
        return m_oBranchingConditionsOfProvidedServices;
    }

    /**
     * Get all the explored provided services.
     * @return all the explored provided services.
     */
    public List<String> getExploredProvidedServices() {
        return m_oExploredProvidedServices;
    }

    /**
     * Get all the IAs of provided services.
     * @return all the IAs of provided services.
     */
    public MultiValueMap getIAsOfProvidedServices() {
        return m_oIAsOfProvidedServices;
    }

    /**
     * Get all the looping counts of provided services.
     * @return all the the looping counts of provided services.
     */
    public MultiValueMap getLoopingCountsOfProvidedServices() {
        return m_oLoopingCountsOfProvidedServices;
    }

    /**
     * Explore the given user interface.
     * @param userInterfaceName the user interface name.
     */
    private void exploreUserInterface(String userInterfaceName) {
        clearCachedData();

        m_strUserInterfaceName = userInterfaceName;

        RMInputModelManager oInputModelManager = RMInputModelManager.getInstance();
        UserInterface oUserInterface = oInputModelManager.getUserInterfaceByName(userInterfaceName);

        String strComponentInstanceName = oUserInterface.getFrom().getFromComponentInstance();
        String strProvidedServiceName = oUserInterface.getFrom().getFromProvidedService();

        m_oTODOProvidedServices.add((new FullProvidedService(strComponentInstanceName, strProvidedServiceName)).toString());

        while (!m_oTODOProvidedServices.isEmpty()) {
            String strFullProvidedServiceName = m_oTODOProvidedServices.removeFirst();
            m_oExploredProvidedServices.add(strFullProvidedServiceName);

            FullProvidedService oFullProvidedService = new FullProvidedService(strFullProvidedServiceName);
            exploreProvidedService(oFullProvidedService.getComponentInstanceName(), oFullProvidedService.getProvidedServiceName());
        }
    }

    /**
     * Explore the given provided service of a given component instance.
     * @param componentInstanceName the component instance name.
     * @param providedServiceName the provided service name.
     */
    private void exploreProvidedService(String componentInstanceName, String providedServiceName) {
        RMInputModelManager oInputModelManager = RMInputModelManager.getInstance();

        UserInterface oUserInterface = oInputModelManager.getUserInterfaceByName(m_strUserInterfaceName);
        String strSystemArchitectureName = oUserInterface.getForSystemArchitecture();

        ComponentInstanceType oComponentInstance = oInputModelManager.getComponentInstance(strSystemArchitectureName, componentInstanceName);
        ServiceImplementationType oServiceImplementation = oInputModelManager.getServiceImplementation(oComponentInstance.getForComponent(), providedServiceName);

        exploreCallPropagation(componentInstanceName, providedServiceName, oServiceImplementation);
    }

    /**
     * Explore the call propagation within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param providedServiceName the given provided service name.
     * @param callPropagation the call propagation.
     */
    private void exploreCallPropagation(String componentInstanceName, String providedServiceName, CallPropagationType callPropagation) {
        if (callPropagation.isSetInternalActivity()) {
            m_oIAsOfProvidedServices.put((new FullProvidedService(componentInstanceName, providedServiceName)).toString(), callPropagation.getInternalActivity());
        } else if (callPropagation.isSetCallingActivity()) {
            exploreCallingActivity(componentInstanceName, callPropagation.getCallingActivity());
        } else if (callPropagation.isSetBranchingStructure()) {
            exploreBranchingStructure(componentInstanceName, providedServiceName, callPropagation.getBranchingStructure());
        } else if (callPropagation.isSetLoopingStructure()) {
            exploreLoopingStructure(componentInstanceName, providedServiceName, callPropagation.getLoopingStructure());
        } else if (callPropagation.isSetSequentialStructure()) {
            exploreSequentialStructure(componentInstanceName, providedServiceName, callPropagation.getSequentialStructure());
        } else if (callPropagation.isSetParallelStructure()) {
            exploreParallelStructure(componentInstanceName, providedServiceName, callPropagation.getParallelStructure());
        } else if (callPropagation.isSetPrimaryBackupFaultToleranceStructure()) {
            explorePrimaryBackupFaultToleranceStructure(componentInstanceName, providedServiceName, callPropagation.getPrimaryBackupFaultToleranceStructure());

        }
    }

    /**
     * Get the only instance.
     * @return the only instance.
     */
    public static synchronized RMSensitivityAnalyser getInstance() {
        if (m_oRMSensitivityAnalyser == null) {
            m_oRMSensitivityAnalyser = new RMSensitivityAnalyser();
        }

        return m_oRMSensitivityAnalyser;
    }

    /**
     * Clear all temp data.
     */
    private void clearCachedData() {
        m_strUserInterfaceName = null;
        m_oExploredProvidedServices.clear();
        m_oIAsOfProvidedServices.clear();
        m_oLoopingCountsOfProvidedServices.clear();
        m_oBranchingConditionsOfProvidedServices.clear();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * Explore the calling activity within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param callingActivity the calling structure.
     */
    private void exploreCallingActivity(String componentInstanceName, CallingActivityType callingActivity) {
        String strCalledService = callingActivity.getCalledService();

        RMInputModelManager oInputModelManager = RMInputModelManager.getInstance();
        String strSystemArchitectureName = oInputModelManager.getUserInterfaceByName(m_strUserInterfaceName).getForSystemArchitecture();

        ComponentConnectorType oComponentConnector = oInputModelManager.getComponentConnectorFor(strSystemArchitectureName, componentInstanceName, strCalledService);
        String strFromComponentInstance = oComponentConnector.getFrom().getFromComponentInstance();
        String strFromProvidedServiceName = oComponentConnector.getFrom().getFromProvidedService();
        String strFullProvidedServiceName = (new FullProvidedService(strFromComponentInstance, strFromProvidedServiceName)).toString();

        if (!m_oExploredProvidedServices.contains(strFullProvidedServiceName)) {
            m_oTODOProvidedServices.add(strFullProvidedServiceName);
        }
    }

    /**
     * Explore the branching structure within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param providedServiceName the given provided service name.
     * @param branchingStructure the branching structure.
     */
    private void exploreBranchingStructure(String componentInstanceName, String providedServiceName, BranchingStructureType branchingStructure) {
        String strBrachingConditions = StringUtils.EMPTY;

        BranchingStructureType.IfPart[] oIfParts = branchingStructure.getIfPartArray();
        for (BranchingStructureType.IfPart oIfPart : oIfParts) {
            if (StringUtils.isEmpty(strBrachingConditions)) {
                strBrachingConditions += oIfPart.getBranchingCondition();
            } else {
                strBrachingConditions += RM_SEMICOLON + oIfPart.getBranchingCondition();
            }

            exploreCallPropagation(componentInstanceName, providedServiceName, oIfPart);
        }

        m_oBranchingConditionsOfProvidedServices.put((new FullProvidedService(componentInstanceName, providedServiceName)).toString(), strBrachingConditions);

        exploreCallPropagation(componentInstanceName, providedServiceName, branchingStructure.getElsePart());
    }

    /**
     * Explore the looping structure within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param providedServiceName the given provided service name.
     * @param loopingStructure the looping structure.
     */
    private void exploreLoopingStructure(String componentInstanceName, String providedServiceName, LoopingStructureType loopingStructure) {
        m_oLoopingCountsOfProvidedServices.put((new FullProvidedService(componentInstanceName, providedServiceName)).toString(), loopingStructure.getLoopCount());

        exploreCallPropagation(componentInstanceName, providedServiceName, loopingStructure.getLoopingPart());
    }

    /**
     * Explore the sequential structure within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param providedServiceName the given provided service name.
     * @param sequentialStructure the sequential structure.
     */
    private void exploreSequentialStructure(String componentInstanceName, String providedServiceName, SequentialStructureType sequentialStructure) {
        for (int i = 0; i < sequentialStructure.getSequentialPartArray().length; i++) {
            exploreCallPropagation(componentInstanceName, providedServiceName, sequentialStructure.getSequentialPartArray()[i]);
        }
    }

    /**
     * Explore the parallel structure within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param providedServiceName the given provided service name.
     * @param parallelStructure the parallel structure.
     */
    private void exploreParallelStructure(String componentInstanceName, String providedServiceName, ParallelStructureType parallelStructure) {
        for (int i = 0; i < parallelStructure.getParallelPartArray().length; i++) {
            exploreCallPropagation(componentInstanceName, providedServiceName, parallelStructure.getParallelPartArray()[i]);
        }
    }

    /**
     * Explore the primary backup fault tolerance structure within a given component instance name and a given provided service name.
     * @param componentInstanceName the given component instance name.
     * @param providedServiceName the given provided service name.
     * @param primaryBackupFaultToleranceStructure the primary backup fault tolerance structure.
     */
    private void explorePrimaryBackupFaultToleranceStructure(String componentInstanceName, String providedServiceName, PrimaryBackupFaultToleranceStructureType primaryBackupFaultToleranceStructure) {
        exploreCallPropagation(componentInstanceName, providedServiceName, primaryBackupFaultToleranceStructure.getPrimaryPart());

        for (int i = 0; i < primaryBackupFaultToleranceStructure.getBackupPartArray().length; i++) {
            exploreCallPropagation(componentInstanceName, providedServiceName, primaryBackupFaultToleranceStructure.getBackupPartArray()[i]);
        }
    }

    class MaxVaryingValuesForReliabilityRelatedProbabilities {

        public static final int RM_SFP = 0;
        public static final int RM_PFP = 1;
        public static final int RM_SEP = 2;
        public static final int RM_NMEP = 3;
        private double m_dMaxVaryingValueForSfp;
        private double m_dMaxVaryingValueForPfp;
        private double m_dMaxVaryingValueForSep;
        private double m_dMaxVaryingValueForNmep;

        /**
         *
         * @param maxVaryingValueForSfp the maximum varying value for Sfp.
         * @param maxVaryingValueForPfp the maximum varying value for Pfp.
         * @param maxVaryingValueForSep the maximum varying value for Sep.
         * @param maxVaryingValueForNmep the maximum varying value for Nmep.
         */
        public MaxVaryingValuesForReliabilityRelatedProbabilities(double maxVaryingValueForSfp, double maxVaryingValueForPfp, double maxVaryingValueForSep, double maxVaryingValueForNmep) {
            m_dMaxVaryingValueForSfp = maxVaryingValueForSfp;
            m_dMaxVaryingValueForPfp = maxVaryingValueForPfp;
            m_dMaxVaryingValueForSep = maxVaryingValueForSep;
            m_dMaxVaryingValueForNmep = maxVaryingValueForNmep;
        }

        /**
         * Get the maximum varying value for the given probability type.
         * @param probabilityType the given probability type (RM_SFP, RM_PFP, RM_SEP or RM_NMEP).
         * @return the maximum varying value for the given probability type.
         */
        public double getMaxVaryingValue(int probabilityType) {
            switch (probabilityType) {
                case RM_SFP:
                    return m_dMaxVaryingValueForSfp;
                case RM_PFP:
                    return m_dMaxVaryingValueForPfp;
                case RM_SEP:
                    return m_dMaxVaryingValueForSep;
                case RM_NMEP:
                    return m_dMaxVaryingValueForNmep;
                default:
                    return 0.0;
            }
        }
    }

    class FullProvidedService {

        public static final String RM_DOT = ".";
        private String m_strComponentInstanceName;
        private String m_strProvidedServiceName;

        /**
         * The constructor.
         * @param fullProvidedServiceName the full provided service name.
         */
        public FullProvidedService(String fullProvidedServiceName) {
            String[] strTemps = StringUtils.split(fullProvidedServiceName, RM_DOT);

            m_strComponentInstanceName = strTemps[0];
            m_strProvidedServiceName = strTemps[1];
        }

        /**
         * The constructor.
         * @param componentInstanceName the component instance name.
         * @param providedServiceName the provided service name.
         */
        public FullProvidedService(String componentInstanceName, String providedServiceName) {
            this.m_strComponentInstanceName = componentInstanceName;
            this.m_strProvidedServiceName = providedServiceName;
        }

        /**
         * Get the component instance name.
         * @return the component instance name.
         */
        public String getComponentInstanceName() {
            return m_strComponentInstanceName;
        }

        /**
         * Get the provided service name.
         * @return the provided service name.
         */
        public String getProvidedServiceName() {
            return m_strProvidedServiceName;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return m_strComponentInstanceName + RM_DOT + m_strProvidedServiceName;
        }
    }
}
